#region --- License & Copyright Notice ---

/*

ID3.NET ID3 tag implementation library

Copyright (c) 2007 Ananth B.
All rights reserved.

The contents of this file are made available under the terms of the
Eclipse Public License v1.0 (the "License") which accompanies this
distribution, and is available at the following URL:
http://opensource.org/licenses/eclipse-1.0.txt

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.

By using this software in any fashion, you are agreeing to be bound by the
terms of the License.

*/

#endregion

using System;
using System.Collections.Generic;
using System.IO;
using ConsoleFx;
using ConsoleFx.Validators;
using Id3.Net;

//ListMusic [-groupby:<album>|<artist>] [-format:<format>] [-s] <mp3 directory>
namespace Id3.Net.Samples.ListMusic
{
    [CommandLine]
    [ParameterUsage(ProgramMode.Normal, MinOccurences = 1, MaxOccurences = 1)]
    [ParameterUsage(ProgramMode.Help, MinOccurences = 0, MaxOccurences = 0)]
    public sealed class Program
    {
        private bool _help;
        private string _groupByFields = "Artist";
        private string _format = "{Artist} - {Title}";
        private bool _recurse = false;
        private string _mp3Directory;

        [ExecutionPoint(ProgramMode.Normal)]
        public int Execute(string[] parameters)
        {
            Console.WriteLine("Group by: {0}", _groupByFields);
            Console.WriteLine("Format: {0}", _format);
            Console.WriteLine("Recurse subdirs: {0}", _recurse);
            Console.WriteLine("MP3 Directory: {0}", this.Mp3Directory);
            return 0;
        }

        [Switch("?", MinOccurences = 0, MaxOccurences = 1, MinParameters = 0, MaxParameters = 0)]
        [SwitchUsage(ProgramMode.Normal, SwitchUsage.NotAllowed)]
        [SwitchUsage(ProgramMode.Help, SwitchUsage.Optional)]
        public void HelpSwitch(string[] parameters)
        {
            _help = true;
        }

        [Switch("groupby", ShortName = "g", MinOccurences = 1, MaxOccurences = 1, MinParameters = 1, MaxParameters = 1)]
        [SwitchUsage(ProgramMode.Normal, SwitchUsage.Optional)]
        [SwitchUsage(ProgramMode.Help, SwitchUsage.NotAllowed)]
        [LookupValidator(ParameterIndex.FirstParameter, "Album,Artist", IgnoreCase = true)]
        public void GroupBySwitch(string[] parameters)
        {
            _groupByFields = parameters[0];
        }

        [Switch("format", ShortName = "f", MinOccurences = 0, MaxOccurences = 1, MinParameters = 1, MaxParameters = 1)]
        [SwitchUsage(ProgramMode.Normal, SwitchUsage.Optional)]
        [SwitchUsage(ProgramMode.Help, SwitchUsage.NotAllowed)]
        public void FormatSwitch(string[] parameters)
        {
            _format = parameters[0];
        }

        [Switch("s", MinOccurences = 0, MaxOccurences = 1, MinParameters = 0, MaxParameters = 0)]
        [SwitchUsage(ProgramMode.Normal, SwitchUsage.Optional)]
        [SwitchUsage(ProgramMode.Help, SwitchUsage.NotAllowed)]
        public void RecurseSwitch(string[] parameters)
        {
            _recurse = true;
        }

        [ParameterProperty(ProgramMode.Normal, ParameterIndex.FirstParameter)]
        public string Mp3Directory
        {
            get
            {
                return _mp3Directory;
            }
            set
            {
                _mp3Directory = value;
            }
        }

        [ModeSetter]
        public int SetMode(string[] parameters)
        {
            return (_help || parameters.Length == 0) ? ProgramMode.Help : ProgramMode.Normal;
        }

        public static int Main(string[] args)
        {
            return CommandLine.Run<Program>(args);
        }
    }

    public static class Program2
    {
        private const string UnknownArtist = "Unknown Artist";
        private const string UnknownAlbum = "Unknown Album";
        private const string UnknownTrack = "Unknown Track";
        
        private enum GroupBy
        {
            Error,
            None,
            Artist,
            Album,
            AlbumAndArtist,
            ArtistAndAlbum
        }

        private struct Metadata
        {
            private string artist;
            private string album;
            private string title;

            public Metadata(string artist, string title, string album)
            {
                this.artist = artist;
                this.title = title;
                this.album = album;
            }

            public string Album
            {
                get
                {
                    return album;
                }
            }

            public string Artist
            {
                get
                {
                    return artist;
                }
            }

            public string Title
            {
                get
                {
                    return title;
                }
            }
        }

        static GroupBy groupBy = GroupBy.None;
        
        static Dictionary<string, Dictionary<string, Metadata>> groupByAlbumAndArtist = new Dictionary<string, Dictionary<string, Metadata>>();
        static Dictionary<string, List<Metadata>> groupByAlbum = new Dictionary<string, List<Metadata>>();
        static Dictionary<string, List<Metadata>> groupByArtist = new Dictionary<string, List<Metadata>>();
        
        static List<string> untaggedFiles = new List<string>();

        static string GetArtist(Id3v1Tag v1Tag, Id3v2xTag v2xTag)
        {
            if ((v1Tag == null) && (v2xTag == null))
            {
                return UnknownArtist;
            }

            if ((v2xTag != null) && (v2xTag.Artists.Value != string.Empty))
            {
                return v2xTag.Artists.Value;
            }

            if ((v1Tag != null) && (v1Tag.Artists.Value != string.Empty))
            {
                return v1Tag.Artists.Value;
            }

            return UnknownArtist;
        }

        static string GetAlbum(Id3v1Tag v1Tag, Id3v2xTag v2xTag)
        {
            if ((v1Tag == null) && (v2xTag == null))
            {
                return UnknownAlbum;
            }

            if ((v2xTag != null) && (v2xTag.Album.Value != string.Empty))
            {
                return v2xTag.Album.Value;
            }

            if ((v1Tag != null) && (v1Tag.Album.Value != string.Empty))
            {
                return v1Tag.Album.Value;
            }

            return UnknownAlbum;
        }

        static string GetTitle(Id3v1Tag v1Tag, Id3v2xTag v2xTag)
        {
            if ((v1Tag == null) && (v2xTag == null))
            {
                return UnknownTrack;
            }

            if ((v2xTag != null) && (v2xTag.Title.Value != string.Empty))
            {
                return v2xTag.Title.Value;
            }

            if ((v1Tag != null) && (v1Tag.Title.Value != string.Empty))
            {
                return v1Tag.Title.Value;
            }

            return UnknownTrack;
        }

        static void GroupMusic(DirectoryInfo directory)
        {
            foreach (DirectoryInfo subDirectory in directory.GetDirectories())
            {
                GroupMusic(subDirectory);
            }

            foreach (FileInfo file in directory.GetFiles("*.mp3"))
            {
                using (Mp3File mp3 = new Mp3File(file.FullName))
                {
                    if (!mp3.HasTags)
                    {
                        untaggedFiles.Add(file.Name);
                        continue;
                    }

                    Id3v2xTag v2xtag = null;
                    Id3v1Tag v1tag = null;

                    string artist = string.Empty;
                    string album = string.Empty;
                    string title = string.Empty;

                    if (mp3.HasTagOfFamily(Id3TagFamily.FileStartTag))
                    {
                        v2xtag = (Id3v2xTag)mp3.GetTag(Id3TagFamily.FileStartTag);
                    }

                    if (mp3.HasTagOfFamily(Id3TagFamily.FileEndTag))
                    {
                        v1tag = (Id3v1Tag)mp3.GetTag(Id3TagFamily.FileEndTag);
                    }

                    artist = GetArtist(v1tag, v2xtag);
                    album = GetAlbum(v1tag, v2xtag);
                    title = GetTitle(v1tag, v2xtag);

                    switch (groupBy)
                    {
                        #region Group by Album
                        case GroupBy.Album:
                            if (!groupByAlbum.ContainsKey(album))
                            {
                                groupByAlbum.Add(album, new List<Metadata>());
                                groupByAlbum[album].Add(new Metadata(artist, title, album));
                            }
                            else
                            {
                                groupByAlbum[album].Add(new Metadata(artist, title, album));
                            }

                            break;
                        #endregion

                        #region Group by Artist
                        case GroupBy.Artist:

                            if (!groupByArtist.ContainsKey(artist))
                            {
                                groupByArtist.Add(artist, new List<Metadata>());
                                groupByArtist[artist].Add(new Metadata(artist, title, album));
                            }
                            else
                            {
                                groupByArtist[artist].Add(new Metadata(artist, title, album));
                            }

                            break;
                        #endregion
                    }
                }
            }
        }
        
        static void PrintUsage(string error)
        {
            if (error != string.Empty)
            {
                Console.WriteLine(error);
            }
            Console.WriteLine("Usage is ListLibrary [/groupby:artist|album] <directory or file mask>");
        }

        static GroupBy GetGroupBy(string groupBySwitch)
        {
            string[] switches = groupBySwitch.ToUpperInvariant().Replace("/GROUPBY:", "").Split(',');
            GroupBy result = GroupBy.None;

            if (switches.Length == 0)
            {
                PrintUsage("At least one grouping criteria should be specified when using the /groupby switch");
                return GroupBy.Error;
            }

            if (switches.Length > 2)
            {
                PrintUsage("Only two grouping criteria are available, and they cannot be used more than once each");
                return GroupBy.Error;
            }

            if (switches[0] == "ARTIST")
            {
                result = GroupBy.Artist;

                if (switches.Length > 1)
                {
                    if (switches[1] == "ALBUM")
                    {
                        result = GroupBy.ArtistAndAlbum;
                    }
                    else
                    {
                        PrintUsage("Only two grouping criteria are available, and they cannot be used more than once each");
                    }
                }
            }
            
            if (switches[0] == "ALBUM")
            {
                result = GroupBy.Album;

                if (switches.Length > 1)
                {
                    if (switches[1] == "ARTIST")
                    {
                        result = GroupBy.AlbumAndArtist;
                    }
                    else
                    {
                        PrintUsage("Only two grouping criteria are available, and they cannot be used more than once each");
                    }
                }
            }

            return result;
        }

        static void Main2(string[] args)
        {
            DirectoryInfo directory = null;
            
            if (args.Length == 1)
            {
                if (args[0].ToUpperInvariant().StartsWith("/GROUPBY"))
                {
                    PrintUsage("You must specify a directory to list music metadata from");
                }
                else
                {
                    DirectoryInfo inputDirectory = new DirectoryInfo(args[0]);
                    if (!inputDirectory.Exists)
                    {
                        PrintUsage(string.Format("The path {0} does not exist", args[0]));
                    }
                    else
                    {
                        directory = inputDirectory;
                    }
                }
            }

            if (args.Length == 2)
            {
                if (args[0].ToUpperInvariant().StartsWith("/GROUPBY"))
                {
                    groupBy = GetGroupBy(args[0]);

                    if (groupBy == GroupBy.Error)
                    {
                        return;
                    }
                }
                else
                {
                    PrintUsage(string.Empty);
                    
                    return;
                }

                DirectoryInfo inputDirectory = new DirectoryInfo(args[1]);
                
                if (!inputDirectory.Exists)
                {
                    PrintUsage(string.Format("The path {0} does not exist", args[0]));

                    return;
                }
                else
                {
                    directory = inputDirectory;
                }
            }
            else
            {
                PrintUsage(string.Empty);

                return;
            }

            GroupMusic(directory);

            switch (groupBy)
            {
                case GroupBy.Album:

                    foreach (KeyValuePair<string, List<Metadata>> currentAlbum in groupByAlbum)
                    {
                        if (currentAlbum.Key != UnknownAlbum)
                        {
                            Console.WriteLine(string.Format("{0} by {1}", currentAlbum.Key, currentAlbum.Value[0].Artist));

                            foreach (Metadata song in currentAlbum.Value)
                            {
                                Console.WriteLine(string.Format("\t{0}", song.Title));
                            }
                        }
                        else
                        {
                            Console.WriteLine(string.Format("Tracks from {0}s", currentAlbum.Key));

                            foreach (Metadata song in currentAlbum.Value)
                            {
                                Console.WriteLine(string.Format("\t{0} by {1}", song.Title, song.Artist));
                            }
                        }
                    }

                    break;

                case GroupBy.Artist:

                    foreach (KeyValuePair<string, List<Metadata>> currentArtist in groupByArtist)
                    {
                        if (currentArtist.Key != UnknownArtist)
                        {
                            Console.WriteLine(string.Format("Songs by {0}", currentArtist.Key));

                            foreach (Metadata song in currentArtist.Value)
                            {
                                Console.WriteLine(string.Format("\t{0}", song.Title));
                            }
                        }
                        else
                        {
                            Console.WriteLine(string.Format("Tracks by {0}s", currentArtist.Key));

                            foreach (Metadata song in currentArtist.Value)
                            {
                                Console.WriteLine(string.Format("\t{0} by {1}", song.Title, song.Artist));
                            }
                        }
                    }
                    
                    break;
            }


            if (untaggedFiles.Count > 0)
            {
                Console.WriteLine(string.Format("{0} untagged file(s) were also found", untaggedFiles.Count));
                foreach (string filename in untaggedFiles)
                {
                    Console.WriteLine(filename);
                }
            }

            Console.ReadKey();
        }
    }
}